打包和工具链

Author Avatar
子语 2018 - 01 - 05
  • 在其它设备中阅读本文章

打包和工具链

Go程序都会组织成若干组文件,每组文件被称为一个包。这样每个包的代码都可以作为很小的复用单元,以标准库中的http包为例:

net/http/
     cgi/
     cookiejar/
        testdata/
     fcgi/
     httptest/
     httputil/
     pprof/
     testdata/

这些目录将实现HTTP服务器、客户端、测试工具和性能调试工具的相关代码拆分成功能清晰、小的代码单元。以cookiejar包为例,这个包里包含了存储与获取网页会话上的cookie相关代码。每个包都可以单独导入和使用。

所有的go文件,除了空行和注释都应该在第一行声明自己所属的包,每个包都在单独的目录里,不能将多个包放在同一个目录下,也不能把同一包的文件拆分到不同目录中。

包的命名原则

包名应使用简洁、清晰且全小写的名字。

main包

main包具有特殊含义,Go的编译器会将其编译为二进制可执行文件。所有Go编译的可执行文件都必须有一个main包。当编译器发现main包后,会寻找main(),然后创建可执行文件。main()是程序的入口,程序编译时会使用声明main包的代码所在的目录的目录名作为二进制可执行文件的文件名。

package main

import "fmt"  // fmt包提供格式化输出功能

func main()  {
	fmt.Println("Hello World!")
}

此时可在$GOPATH/src/hello目录执行go build,会生成一个二进制文件,该文件会根据系统改变文件形式,比如在windows上会变为hello.exe。此时执行该程序,会在控制台显示Hello World!。如果将包名改为其他,编译器会认为其只是一个包,而不是命令。

导入

import告诉编译器要导入包的路径。如果要导入多个包,可按照如下格式:

import (
  "fmt"
  "strings"  // strings包提供关于字符串的操作,如查找、替换和变换
)

编译器会使用Go环境变量设置的路径,通过引入的相对路径来查找磁盘上的包。开发者创建的包会在GOPATH环境变量指定的目录里查找。GOPATH指订的目录就是开发者的个人工作空间。如果Go安装在/user/local/go,并把GOPATH设置为/home/myproject:/home/mylibraries,编译器会按照以下顺序查找net/http包:

/usr/local/go/src/pkg/net/http
/home/myproject/src/net/http
/home/mylibraries/src/net/http

如果编译器找到满足的包,就会停止查找。编译器会优先查找Go的安装目录,然后才是GOPATH中的目录。

远程导入

目前趋势是,使用分布式版本控制系统(Distributed Version Control Systems, DVCS)分享代码。Go的工具链支持远程导入。如:

import "github.com/spf13/viper"	

编译程序时,编译器会通过go get根据指定的URL获取包。

命名导入

当要导入的多个包具有相同名字时,可以为其定义别名,格式如下:

package main

import (
  "fmt"
  myfmt "mylib.fmt"
)

func main() {
  fmt.Println("Hello")
  myfmt.Println("Hello")
}

当导入一个包,但不使用它,Go编译器会编译失败,并输出一个错误。这样会避免代码变得臃肿。有时可能需要导入一个包,但不需要引用该包的标识符,此时可以使用空白标识符_来重命名这个导入。

空白标识符 下划线_在Go中被称为空白标识符,用来i抛弃不想继续使用的值,比如忽略函数返回的不需要的值。

函数 init

每个包都可以包含任意多个init函数,这些函数会在程序开始执行的时间被调用。init函数会在main函数之前执行。init函数用在设置包、初始化变量或者其他要在程序运行前优先完成的引导工作。

例子:以数据库驱动为例,database下的驱动在启动时执行init函数会被自身注册到sql包中,因为sql包在编译时并不知道这些驱动的存在,等启动后sql才会调用这些驱动,其代码实现如下:

package postgres

import "database/sql"

func init()  {
	// 创建一个postgres驱动的实例,此处不进行细节定义
	sql.Register("postgres", new(PostgresDriver)) 
}

这段示例代码包含在PostgreSQL数据库的驱动中,如果程序导入了这个包,就会调用init函数,促使驱动注册到GO的sql包中成为一个可用驱动。

package main

import (
	"database/sql"
	_ "./postgres"
)

func main() {
	sql.Open("postgres", "mydb")
}

在使用这个新的数据库驱动写程序时,使用空白标识符导入包,以便新的驱动会包含到sql包中,这样就可以让init函数被调用运行,且不会由于没有使用这个包中方法而产生错误。

使用Go的工具

λ go
Go is a tool for managing Go source code.

Usage:

        go command [arguments]

The commands are:

        build       compile packages and dependencies
        clean       remove object files
        doc         show documentation for package or symbol
        env         print Go environment information
        bug         start a bug report
        fix         run go tool fix on packages
        fmt         run gofmt on package sources
        generate    generate Go files by processing source
        get         download and install packages and dependencies
        install     compile and install packages and dependencies
        list        list packages
        run         compile and run Go program
        test        test packages
        tool        run specified go tool
        version     print Go version
        vet         run go tool vet on packages

Use "go help [command]" for more information about a command.

Additional help topics:

        c           calling between Go and C
        buildmode   description of build modes
        filetype    file types
        gopath      GOPATH environment variable
        environment environment variables
        importpath  import path syntax
        packages    description of package lists
        testflag    description of testing flags
        testfunc    description of testing functions

Use "go help [topic]" for more information about that topic.
  • 编译源代码使用go build xx.go
  • 删除编译生成的可执行文件go clean xx.go

范例:编写代码获取text文本中的内容长度。

---------------------- count.go ------------------------------
package words

import "strings"

func CountWords(text string) (count int) {
    // 计算文本长度
	count = len(strings.Fields(text))
	return
}
---------------------- main.go -------------------------------
package main

import (
	"os"
	"io/ioutil"
	"../words"   // 导入上述的包
	"fmt"
)

func main()  {
	filename := os.Args[1]

	contents, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("There was an error opening the file:", err)
		return
	}

	text := string(contents)
	count := words.CountWords(text)
	fmt.Printf("There are %d words in your text. \n", count)
}

直接使用go build,go编译器会默认使用当前目录编译。

可以指定包编译go build xxx/xxx

也可以使用通配符...,会编译目录下所有包go build xx/xx/...

要执行程序,需要先编译,虽然执行生成的可执行文件。此处可以使用go run完成一次性完成这两步,该命令会先构建main.go里包含的程序,然后执行创建后的程序。

Go开发工具

go vet

go vet会帮助开发人员检测代码常见的错误,可以捕获以下类型错误:

  • Printf类函数调用时,类型匹配错误的参数;
  • 定义常用方法时,方法签名的错误
  • 错误的结构标签
  • 没有指定字段名的结构字面量
package main

import "fmt"

func main() {
	fmt.Printf("There is dogs", 3)
}

上述程序要输出一个整数3,但是格式化字符串中没有对应的格式化参数,执行go vet会得到如下信息:

λ go vet main.go
main.go:6: no formatting directive in Printf call

go fmt

go fmt会自动格式化源代码文件并保存。

Go语言开发文档

  • 从命令行获取文档

比如要了解fmt包相关信息,就可以使用命令 go doc fmt。就能获取fmt包的详细信息,比如函数,格式化参数等。

  • 浏览器查看文档

使用godoc -http=:port,此时就可以打开浏览器输入ip:port查看Go的文档

  • 给代码编写文档

GO文档工具也支持开发人员自己写的代码。如果开发人员按照规则来编写代码,这些代码也会自动包含在godoc生成的文档中。

用户可以在标识符前,将文档内容作为注释添加到代码中。注释可以以//开头,也可以以/**/。此外还可以在包中添加doc.go,其包名与包一致,将包的介绍使用注释加载包名声明之前。

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Go/Go Base/打包和工具链/